package socket

import (
	"ac-julink/tool/config"
	"net"
	"os"
	"syscall"
)

const (
	TCPUserTimeout = 18
)

var socketOpt *SocketOptions

type SocketOptions struct {
	// socket参数
	connReadBuffer  int
	connWriteBuffer int
	connNoDelay     bool
	sendTimeout     int
	keepAlive       int
	//	keepAlivePeriod    int
	keepAliveIdel      int
	keepAlive_interval int
	keep_count         int
}

func InitSocketOptions() {
	socketOpt = newSocketOptions()
}

func ApplyTcp(conn *net.TCPConn) (int, error) {

	if socketOpt.connReadBuffer >= 0 {
		conn.SetReadBuffer(socketOpt.connReadBuffer)
	}

	if socketOpt.connWriteBuffer >= 0 {
		conn.SetWriteBuffer(socketOpt.connWriteBuffer)
	}
	conn.SetNoDelay(socketOpt.connNoDelay)
	//	conn.SetKeepAlive(socketOpt.keepAlive)
	//	conn.SetKeepAlivePeriod(time.Duration(socketOpt.keepAlivePeriod) * time.Second)

	return setTCPOption(conn)
}

func setTCPOption(conn *net.TCPConn) (int, error) {
	f, err := conn.File()
	if err != nil {
		return 0, err
	}
	defer f.Close()

	fd := int(f.Fd())
	// conn.File() set the connection block, we must reset it to nonblock!!!
	if err = syscall.SetNonblock(fd, true); err != nil {
		return 0, os.NewSyscallError("setnonblock", err)
	}
	err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
	if err != nil {
		return 0, os.NewSyscallError("setsockopt SO_KEEPALIVE", err)
	}
	err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, socketOpt.keepAlive)
	if err != nil {
		return 0, os.NewSyscallError("setsockopt SO_KEEPALIVE", err)
	}
	err = syscall.SetsockoptInt(fd, syscall.SOL_TCP, syscall.TCP_KEEPIDLE, socketOpt.keepAliveIdel)
	if err != nil {
		return 0, os.NewSyscallError("setsockopt TCP_KEEPIDLE", err)
	}
	err = syscall.SetsockoptInt(fd, syscall.SOL_TCP, syscall.TCP_KEEPINTVL, socketOpt.keepAlive_interval)
	if err != nil {
		return 0, os.NewSyscallError("setsockopt TCP_KEEPINTVL", err)
	}
	err = syscall.SetsockoptInt(fd, syscall.SOL_TCP, syscall.TCP_KEEPCNT, socketOpt.keep_count)
	if err != nil {
		return 0, os.NewSyscallError("setsockopt TCP_KEEPCNT", err)
	}
	return fd, os.NewSyscallError("setsockopt TCPUserTimeout",
		syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, TCPUserTimeout, socketOpt.sendTimeout*1000))
}

func newSocketOptions() *SocketOptions {
	connReadBuffer, ok := config.GetConfigIntValue("WGNS", "tcp.read.buffer")
	if !ok {
		connReadBuffer = -1
	}
	connWriteBuffer, ok := config.GetConfigIntValue("WGNS", "tcp.write.buffer")
	if !ok {
		connWriteBuffer = -1
	}
	noDelayFlag, ok := config.GetConfigIntValue("WGNS", "tcp.nodelay")
	if !ok {
		noDelayFlag = 1
	}
	sendTimeout, ok := config.GetConfigIntValue("WGNS", "tcp.send.timeout")
	if !ok {
		sendTimeout = 60
	}
	keepAlive, ok := config.GetConfigIntValue("WGNS", "tcp.keepalive")
	if !ok {
		keepAlive = 1
	}
	keepAliveIdel, ok := config.GetConfigIntValue("WGNS", "tcp.keepalive.idel")
	if !ok {
		keepAliveIdel = 360
	}
	keepAlive_interval, ok := config.GetConfigIntValue("WGNS", "tcp.keepalive.interval")
	if !ok {
		keepAlive_interval = 5
	}
	keep_count, ok := config.GetConfigIntValue("WGNS", "tcp.keepalive.count")
	if !ok {
		keep_count = 3
	}
	return &SocketOptions{
		connWriteBuffer: connWriteBuffer,
		connReadBuffer:  connReadBuffer,
		connNoDelay:     noDelayFlag == 1,
		sendTimeout:     sendTimeout,
		keepAlive:       keepAlive,
		//		keepAlivePeriod: keepAlivePeriod,
		keepAliveIdel:      keepAliveIdel,
		keepAlive_interval: keepAlive_interval,
		keep_count:         keep_count,
	}
}
